const options = {
  createElement(tag) { 
    return document.createElement(tag)
  },
  setElementText(el, text) {
    el.textContent = text
  },
  insert(el, parent, anchor = null) { 
    parent.insertBefore(el, anchor)
  },
  patchProps(el, key, prevValue, nextValue) {
    if (key === 'class') { 
      el.className = nextValue || ""
    }
    else if (shouldSetAsProps(el, key, nextValue)) {
      const type = typeof el[key]
      
      if (type === 'boolean' && nextValue === '') {
        el[key] = true
      }
      else { 
        el[key] = nextValue
      }
    }
    else { 
      el.setAttribute(key, nextValue)
    }
  }
}

function shouldSetAsProps(el, key, value) { 
  if (key === 'form' && el.tagName === 'INPUT') { 
    return false;
  }
  
  return key in el;
}

// 设置class其实有三种方式
// el.setAttribute
// el.className
// el.classList

function createRenderer(options) { 

  const { 
    createElement,
    setElementText,
    insert,
    patchProps
  } = options

  function render(vnode, container) { 
    if (vnode) {
      patch(container._vnode, vnode, container)
    }
    else { 
      if (container._vnode) { 
        // 旧的Vnode存在，但是新的vnode不存在，说明要进行卸载操作
        // 现在简单的将container中的内容清空
        container.innerHTML = "";
      }
    }

    container._vnode = vnode;
    
  }

  function patch(oldVnode, newVnode, container) {
    // 如果旧节点不存在，就意味着是挂载，调用mountElement函数完成挂载
    if (!oldVnode) {
      mountElement(newVnode, container)
    }
    else { 
      // 如果oldVnode存在，就是更新, 暂时省略...
    }
  }

  function mountElement(vnode, container) { 
    // 创建元素
    const el = createElement(vnode.type)
    if (typeof vnode.children === 'string') {
      // 如果子节点是字符串，说明是文本节点，直接挂载
      setElementText(el, vnode.children)
    }
    else if (Array.isArray(vnode.children)) { 
      vnode.children.forEach(child => { 
        patch(null, child, el)
      }) 
    }

    // 注意挂载属性的时候，需要注意的问题
    // HTML标签属性名字和DOM API属性名字可能不一样，甚至有可能有些有，有些没有
    // <div aria-valuenow="75"></div>
    // 有些时候HTML上的属性名和DOM API属性名可能会造成误解
    // <input value="foo">  ---> bar
    // DOM API  ---->  input.value = 'foo'    input.value这种获取，能够获取到用户修改之后的值 bar
    // input.getAttribute('value')  ---->  foo 这种获取，获取不到用户修改之后的值  foo

    // 有时候DOM API属性并不能直接进行设置
    // <form id="form1"></form>
    // <input form="form1">

    if (vnode.props) { 
      for (const key in vnode.props) { 
        const value = vnode.props[key]
        patchProps(el, key, null, value)
        
      }
    }

    insert(el, container)
  }

  // 其他可能会用到的函数...
  return {
    render
    //...其他的函数
  }
}

/* 
class: "btn btn-primary"
<btn class="btn btn-primary"></btn> 


vue模板中---对象
const cls = {btn:true, 'btn-primary':true}
<btn :class="cls"></btn>

vue模板中---数组
const clsArr = ['btn', 'btn-primary', {baz:true}]
<btn :class="clsArr"></btn>

*/
const cls = { btn: true, 'btn-primary': true }
const clsArr = ['btn', 'btn-primary', { baz: true }]

const isString = (val) => { 
  return typeof val === 'string'
}

const isObject = (val) => {
  return val !== null && typeof val === 'object'
}

const isArray = Array.isArray

function normalizeClass(value) { 
  let res = "";

  if (isString(value)) {
    res = value;
  }
  else if (isArray(value)) {
    for (let i = 0; i < value.length; i++) {
      const normalized = normalizeClass(value[i])
      if (normalized) { 
        res += normalized + " "
      }
    }
  }
  else if (isObject(value)) { 
    for (const name in value) { 
      if (value[name]) { 
        res += name + " "
      }
    }

  }


  return res;
}


const vnode = {
  type: "h1",
  props: {
    id: 'foo',
    class: normalizeClass(cls)
  },
  children: [
    {
      type: "p",
      children: "hello world"
    }
  ]
}

const renderer = createRenderer(options);
// 首次渲染
renderer.render(vnode, document.getElementById('app'))

// // 第二次渲染
// renderer.render(newVnode, document.getElementById('app'))

// // 第三次渲染
// renderer.render(null, document.getElementById('app'))

